#!/bin/bash

set -euo pipefail

UPGRADE_TMP_DIR='/root/upgrade_tmp'
TARGET_VERSION=9
DISABLED_PLUGINS="--disableplugin=needs-restarting"
STAGE_STATUSES_DIR='/var/run/opencloudos-upgrade-statuses'

## start 日志及状态记录函数  ##
setup_log_files() {
	exec > >(tee /var/log/os-upgrade.log)
	exec 5>/var/log/os-upgrade.debug.log
	BASH_XTRACEFD=5
}

save_status_of_stage() {
	if [[ 0 != "$(id -u)" ]]; then
		return 0
	fi
	local -r stage_name="${1}"
	if [[ ! -d "${STAGE_STATUSES_DIR}" ]]; then
		mkdir -p "${STAGE_STATUSES_DIR}"
	fi
	touch "${STAGE_STATUSES_DIR}/${stage_name}"
}

# Get a status of a stage for continue of it
get_status_of_stage() {
	if [[ 0 != "$(id -u)" ]]; then
		return 1
	fi
	local -r stage_name="${1}"
	if [[ ! -d "${STAGE_STATUSES_DIR}" ]]; then
		return 1
	fi
	if [[ ! -f "${STAGE_STATUSES_DIR}/${stage_name}" ]]; then
		return 1
	fi
	return 0
}

is_upgrade_completed() {
	if get_status_of_stage "completed"; then
		printf '\n\033[0;32mUpgrade to OpenCloudOS was already completed\033[0m\n'
		exit 0
	fi
}

report_step_done() {
	local -r message="${1}"
	printf '\033[0;32m%-70sOK\033[0m\n' "${message}"
}

# Reports a failed step using a red color.
report_step_error() {
	local -r message="${1}"
	local -r trace="${2:-}"
	printf '\033[0;31m%-70sERROR\033[0m\n' "${message}" 1>&2
	if [[ -n "${trace}" ]]; then
		echo "${trace}" | while read -r line; do
			printf '    %s\n' "${line}" 1>&2
		done
	fi
}

## end 日志及状态记录函数  ##

## start 配置及一些检查函数，公共函数   ##

cleanup_tmp_dir() {
	rm -fr "${1:?}"
}

assert_run_as_root() {
	if [[ $(id -u) -ne 0 ]]; then
		report_step_error 'Check root privileges' \
			'Upgrade tool must be run as root'
		exit 2
	fi
	report_step_done 'Check root privileges'
}

pre_upgrade() {
	if get_status_of_stage "pre_upgrade"; then
		return 0
	fi
	# 因为oc9,源本身不具备模块话功能，直接remove
	rm -f /etc/dnf/modules.d/*.module
	## 配置dnf.conf
	cat >"${UPGRADE_TMP_DIR}/dnf.conf" <<EOF
[main]
gpgcheck=1
max_parallel_downloads=10
installonly_limit=3
clean_requirements_on_remove=True
best=False
skip_if_unavailable=True
deltarpm=false
zchunk=False

[BaseOS-upgrade]
name=OpenCloudOS \$releasever - BaseOS-upgrade
baseurl=https://mirrors.opencloudos.tech/opencloudos/\$releasever/BaseOS/\$basearch/os/
gpgcheck=0
enabled=1

[AppStream-upgrade]
name=OpenCloudOS \$releasever - AppStream-upgrade
baseurl=https://mirrors.opencloudos.tech/opencloudos/\$releasever/AppStream/\$basearch/os/
gpgcheck=0
enabled=1
EOF
	report_step_done "pre_upgrade"
	save_status_of_stage "pre_upgrade"
}

dnf_shell_cmd() {
	LANG=C dnf -c "${UPGRADE_TMP_DIR}/dnf.conf" -q -y shell \
		--disablerepo="*" --enablerepo="*-upgrade" \
		--releasever="${TARGET_VERSION}" \
		--setopt=deltarpm=false $DISABLED_PLUGINS \
		--nogpgcheck --allowerasing --nobest "${UPGRADE_TMP_DIR}/dnf_shell_data"
}

create_dnf_shell_data() {
	if get_status_of_stage "create_dnf_shell_data"; then
		return 0
	fi
	echo distro-sync >"${UPGRADE_TMP_DIR}/dnf_shell_data"
	set +e
	dnf_shell_cmd 2>"${UPGRADE_TMP_DIR}/conflicts.orig"
	grep conflicts "${UPGRADE_TMP_DIR}/conflicts.orig" >"${UPGRADE_TMP_DIR}/conflicts"
	set -e

	{
		# 存在冲突的软件包
		echo remove NetworkManager-bluetooth
		echo remove NetworkManager-team
		echo remove NetworkManager-wifi
		echo remove NetworkManager-wwan
		echo swap mozjs60 mozjs102
		if rpm -q --quiet dejavu-sans-fonts; then
			echo swap dejavu-*-fonts dejavu-fonts
		fi
		awk '{print "swap " $NF " " $6}' "${UPGRADE_TMP_DIR}/conflicts" | sort -u
		# uefi需要安装shim
		if [ -d "/sys/firmware/efi/" ]; then
			echo install shim
		fi
		echo
	} >>"${UPGRADE_TMP_DIR}/dnf_shell_data"

	report_step_done "create dnf shell_data"
	save_status_of_stage "create_dnf_shell_data"
}

## end 配置及一些检查函数，公共函数  ##

## start 升级过程函数，包括升级前后异常处理等  ##

rebuild_rpmdb() {
	if get_status_of_stage "rebuild_rpmdb"; then
		return 0
	fi

	## 重建rpm数据库，因为oc9 默认数据库路径有变更
	if [ ! -d /usr/lib/sysimage/rpm ]; then
		mv /var/lib/rpm /usr/lib/sysimage/
	fi
	if [ -d /var/lib/rpm ]; then
		touch /var/lib/rpm/.migratedb
	fi
	if [ ! -d /var/lib/rpm ] && [ -d /usr/lib/sysimage/rpm ] && [ ! -f /usr/lib/sysimage/rpm/.rpmdbdirsymlink_created ]; then
		ln -sfr /usr/lib/sysimage/rpm /var/lib/rpm
		touch /usr/lib/sysimage/rpm/.rpmdbdirsymlink_created
	fi
	sed -i 's|^%_dbpath	.*|%_dbpath		%{_usr}/lib/sysimage/rpm|g' /usr/lib/rpm/macros
	rpm --rebuilddb
	report_step_done "rebuild rpmdb"
	save_status_of_stage "rebuild_rpmdb"
}

dnf_upgrade_before_upgrade() {
	if get_status_of_stage "dnf_upgrade_before_upgrade"; then
		return 0
	fi
	dnf -c "${UPGRADE_TMP_DIR}/dnf.conf" upgrade -y -q \
		--disablerepo="*" --enablerepo="*-upgrade"
	dnf -c "${UPGRADE_TMP_DIR}/dnf.conf" clean -q dbcache metadata
	report_step_done "dnf_upgrade_before_upgrade"
	save_status_of_stage "dnf_upgrade_before_upgrade"
}

do_upgrade() {
	if get_status_of_stage "do_upgrade"; then
		return 0
	fi
	if rpm -q opencloudos-release; then
		rpm -e --nodeps opencloudos-release
	fi

	chmod -x /usr/bin/firewall-cmd
	if ! dnf_shell_cmd; then
		report_step_error "do upgrade"
		exit 1
	fi
	report_step_done "do upgrade"
	chmod +x /usr/bin/firewall-cmd
	rpm --rebuilddb

	save_status_of_stage "do_upgrade"
}

upgrade_installed_group() {
	if get_status_of_stage "upgrade installed group"; then
		return 0
	fi
	# 升级已安装的组
	LANG=C dnf -c "${UPGRADE_TMP_DIR}/dnf.conf" -q \
		--disablerepo="*" --enablerepo="*-upgrade" \
		--releasever="${TARGET_VERSION}" grouplist --installed |
		grep -v Installed | sed 's/^   /groupupdate "/g' |
		sed 's/$/"/g' >"${UPGRADE_TMP_DIR}/dnf_shell_data"

	if ! dnf_shell_cmd; then
		report_step_error "upgrade installed group"
		exit 1
	fi

	report_step_done "upgrade installed group"
	save_status_of_stage "upgrade_installed_group"
}

post_upgrade() {
	if get_status_of_stage "post_upgrade"; then
		return 0
	fi
	rpm --rebuilddb

	echo 9 >/etc/dnf/vars/releasever
	rm -f /etc/dnf/vars/releasever.rpmnew
	echo PermitRootLogin yes | tee -a /etc/ssh/sshd_config
	systemctl restart sshd.service
	save_status_of_stage "post_upgrade"
}

cleanup_cache() {
	if get_status_of_stage "cleanup_cache"; then
		return 0
	fi
	rm -rf /var/cache/yum/* /var/cache/dnf/* /var/cache/PackageKit/*
	save_status_of_stage "cleanup_cache"
}

reset_service_priorities() {
	if get_status_of_stage "reset_service_priorities"; then
		return 0
	fi
	systemctl daemon-reload
	systemctl enable --now dbus-broker.service
	systemctl restart dbus
	systemctl restart dbus-org.freedesktop.login1.service
	systemctl preset-all || true
	report_step_done "reset service priorities"
	save_status_of_stage "reset_service_priorities"
}
## end 升级过程函数，包括升级前后异常处理等  ##

################################ START OF THE CODE ############################

main() {
	assert_run_as_root
	setup_log_files
	set -x
	is_upgrade_completed
	mkdir "${UPGRADE_TMP_DIR}"
	# shellcheck disable=SC2064
	trap "cleanup_tmp_dir ${UPGRADE_TMP_DIR}" EXIT
	pre_upgrade
	rebuild_rpmdb
	dnf_upgrade_before_upgrade
	create_dnf_shell_data
	do_upgrade
	post_upgrade
	cleanup_cache
	reset_service_priorities
	report_step_done "You successfully upgraded to OpenCloudOS $TARGET_VERSION"
	report_step_done "Reboot is strongly suggested."
	save_status_of_stage "completed"
	set +x
}

main
